﻿using UnityEngine;

namespace FPLibrary
{
	public class FPTransform
	{
		public FPVector3 Position { get; private set; }

		public FPQuaternion Rotation { get; private set; }

		public FPVector3 Scale { get; private set; }

		public FPVector3 Forward => FPVector3.Transform(FPVector3.forward, FPMatrix.CreateFromQuaternion(Rotation));

		public FPVector3 Right => FPVector3.Transform(FPVector3.right, FPMatrix.CreateFromQuaternion(Rotation));

		public FPVector3 Up => FPVector3.Transform(FPVector3.up, FPMatrix.CreateFromQuaternion(Rotation));

		public FPVector3 EulerAngles => Rotation.eulerAngles;

		public FPTransform() { }

		public FPTransform(FPTransform fpTransform)
		{
			SetPosition(fpTransform.Position);
			SetRotation(fpTransform.Rotation);
			SetScale(fpTransform.Scale);
		}

		public void SetPosition(FPVector3 position) =>
			Position = position;

		public void SetRotation(FPQuaternion rotation) =>
			Rotation = rotation;

		public void SetScale(FPVector3 scale) =>
			Scale = scale;

		public void LookAt(FPTransform other) =>
			LookAt(other.Position);

		public void LookAt(FPVector3 target) =>
			Rotation = FPQuaternion.CreateFromMatrix(FPMatrix.CreateFromLookAt(Position, target));

		public void Translate(Fix64 x, Fix64 y, Fix64 z) =>
			Translate(x, y, z, Space.World);

		public void Translate(Fix64 x, Fix64 y, Fix64 z, Space relativeTo) =>
			Translate(new FPVector3(x, y, z), relativeTo);

		public void Translate(Fix64 x, Fix64 y, Fix64 z, FPTransform relativeTo) =>
			Translate(new FPVector3(x, y, z), relativeTo);

		public void Translate(FPVector3 translation) =>
			Translate(translation, Space.Self);

		public void Translate(FPVector3 translation, Space relativeTo)
		{
			if (relativeTo == Space.Self)
				Translate(translation, this);

			else
				Position += translation;
		}

		public void Translate(FPVector3 translation, FPTransform relativeTo) =>
			Position += FPVector3.Transform(translation, FPMatrix.CreateFromQuaternion(relativeTo.Rotation));

		public void RotateAround(FPVector3 point, FPVector3 axis, Fix64 angle)
		{
			var vector2 = FPVector3.Transform(Position - point, FPMatrix.AngleAxis(angle * Fix64.Deg2Rad, axis));
			Position = point + vector2;
			Rotate(axis, angle);
		}

		public void RotateAround(FPVector3 axis, Fix64 angle) =>
			Rotate(axis, angle);

		public void Rotate(Fix64 xAngle, Fix64 yAngle, Fix64 zAngle) =>
			Rotate(new FPVector3(xAngle, yAngle, zAngle), Space.Self);

		public void Rotate(Fix64 xAngle, Fix64 yAngle, Fix64 zAngle, Space relativeTo) =>
			Rotate(new FPVector3(xAngle, yAngle, zAngle), relativeTo);

		public void Rotate(FPVector3 eulerAngles) =>
			Rotate(eulerAngles, Space.Self);

		public void Rotate(FPVector3 axis, Fix64 angle) =>
			Rotate(axis, angle, Space.Self);

		public void Rotate(FPVector3 axis, Fix64 angle, Space relativeTo)
		{
			var result = (relativeTo == Space.Self) ? Rotation * FPQuaternion.AngleAxis(angle, axis) : FPQuaternion.AngleAxis(angle, axis) * Rotation;
			result.Normalize();
			Rotation = result;
		}

		public void Rotate(FPVector3 eulerAngles, Space relativeTo)
		{
			var result = (relativeTo == Space.Self) ? Rotation * FPQuaternion.Euler(eulerAngles) : FPQuaternion.Euler(eulerAngles) * Rotation;
			result.Normalize();
			Rotation = result;
		}
	}
}
